1 /*
2 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.font;
27
28 import java.lang.ref.Reference;
29 import java.awt.FontFormatException;
30 import java.awt.geom.GeneralPath;
31 import java.awt.geom.Point2D;
32 import java.awt.geom.Rectangle2D;
33 import java.io.File;
34 import java.nio.ByteBuffer;
35 import sun.java2d.Disposer;
36 import sun.java2d.DisposerRecord;
37
38 import java.io.IOException;
39 import java.security.AccessController;
40 import java.security.PrivilegedActionException;
41 import java.security.PrivilegedExceptionAction;
42
43 public abstract class FileFont extends PhysicalFont {
44
45 protected boolean useJavaRasterizer = true;
46
47 /* I/O and file operations are always synchronized on the font
48 * object. Two threads can be accessing the font and retrieving
49 * information, and synchronized only to the extent that filesystem
50 * operations require.
51 * A limited number of files can be open at a time, to limit the
52 * absorption of file descriptors. If a file needs to be opened
53 * when there are none free, then the synchronization of all I/O
54 * ensures that any in progress operation will complete before some
55 * other thread closes the descriptor in order to allocate another one.
56 */
57 // NB consider using a RAF. FIS has finalize method so may take a
58 // little longer to be GC'd. We don't use this stream at all anyway.
59 // In fact why increase the size of a FileFont object if the stream
60 // isn't needed ..
61 //protected FileInputStream stream;
62 //protected FileChannel channel;
63 protected int fileSize;
64
65 protected FontScaler scaler;
66
67 /* The following variables are used, (and in the case of the arrays,
68 * only initialised) for select fonts where a native scaler may be
69 * used to get glyph images and metrics.
70 * glyphToCharMap is filled in on the fly and used to do a reverse
71 * lookup when a FileFont needs to get the charcode back from a glyph
72 * code so it can re-map via a NativeGlyphMapper to get a native glyph.
73 * This isn't a big hit in time, since a boolean test is sufficient
74 * to choose the usual default path, nor in memory for fonts which take
75 * the native path, since fonts have contiguous zero-based glyph indexes,
76 * and these obviously do all exist in the font.
77 */
78 protected boolean checkedNatives;
79 protected boolean useNatives;
80 protected NativeFont[] nativeFonts;
81 protected char[] glyphToCharMap;
82 /*
83 * @throws FontFormatException - if the font can't be opened
84 */
85 FileFont(String platname, Object nativeNames)
86 throws FontFormatException {
87
88 super(platname, nativeNames);
89 }
90
91 FontStrike createStrike(FontStrikeDesc desc) {
92 if (!checkedNatives) {
93 checkUseNatives();
94 }
95 return new FileFontStrike(this, desc);
96 }
97
98 protected boolean checkUseNatives() {
99 checkedNatives = true;
100 return useNatives;
101 }
102
103 /* This method needs to be accessible to FontManager if there is
104 * file pool management. It may be a no-op.
105 */
106 protected abstract void close();
107
108
109 /*
110 * This is the public interface. The subclasses need to implement
111 * this. The returned block may be longer than the requested length.
112 */
113 abstract ByteBuffer readBlock(int offset, int length);
114
115 public boolean canDoStyle(int style) {
116 return true;
117 }
118
119 void setFileToRemove(File file, CreatedFontTracker tracker) {
120 Disposer.addObjectRecord(this,
121 new CreatedFontFileDisposerRecord(file, tracker));
122 }
123
124 /* This is called when a font scaler is determined to
125 * be unusable (ie bad).
126 * We want to replace current scaler with NullFontScaler, so
127 * we never try to use same font scaler again.
128 * Scaler native resources could have already been disposed
129 * or they will be eventually by Java2D disposer.
130 * However, it should be safe to call dispose() explicitly here.
131 *
132 * For safety we also invalidate all strike's scaler context.
133 * So, in case they cache pointer to native scaler
134 * it will not ever be used.
135 *
136 * It also appears desirable to remove all the entries from the
137 * cache so no other code will pick them up. But we can't just
138 * 'delete' them as code may be using them. And simply dropping
139 * the reference to the cache will make the reference objects
140 * unreachable and so they will not get disposed.
141 * Since a strike may hold (via java arrays) native pointers to many
142 * rasterised glyphs, this would be a memory leak.
143 * The solution is :
144 * - to move all the entries to another map where they
145 * are no longer locatable
146 * - update FontStrikeDisposer to be able to distinguish which
147 * map they are held in via a boolean flag
148 * Since this isn't expected to be anything other than an extremely
149 * rare maybe it is not worth doing this last part.
150 */
151 synchronized void deregisterFontAndClearStrikeCache() {
152 SunFontManager fm = SunFontManager.getInstance();
153 fm.deRegisterBadFont(this);
154
155 for (Reference strikeRef : strikeCache.values()) {
156 if (strikeRef != null) {
157 /* NB we know these are all FileFontStrike instances
158 * because the cache is on this FileFont
159 */
160 FileFontStrike strike = (FileFontStrike)strikeRef.get();
161 if (strike != null && strike.pScalerContext != 0L) {
162 scaler.invalidateScalerContext(strike.pScalerContext);
163 }
164 }
165 }
166 scaler.dispose();
167 scaler = FontScaler.getNullScaler();
168 }
169
170 StrikeMetrics getFontMetrics(long pScalerContext) {
171 try {
172 return getScaler().getFontMetrics(pScalerContext);
173 } catch (FontScalerException fe) {
174 scaler = FontScaler.getNullScaler();
175 return getFontMetrics(pScalerContext);
176 }
177 }
178
179 float getGlyphAdvance(long pScalerContext, int glyphCode) {
180 try {
181 return getScaler().getGlyphAdvance(pScalerContext, glyphCode);
182 } catch (FontScalerException fe) {
183 scaler = FontScaler.getNullScaler();
184 return getGlyphAdvance(pScalerContext, glyphCode);
185 }
186 }
187
188 void getGlyphMetrics(long pScalerContext, int glyphCode, Point2D.Float metrics) {
189 try {
190 getScaler().getGlyphMetrics(pScalerContext, glyphCode, metrics);
191 } catch (FontScalerException fe) {
192 scaler = FontScaler.getNullScaler();
193 getGlyphMetrics(pScalerContext, glyphCode, metrics);
194 }
195 }
196
197 long getGlyphImage(long pScalerContext, int glyphCode) {
198 try {
199 return getScaler().getGlyphImage(pScalerContext, glyphCode);
200 } catch (FontScalerException fe) {
201 scaler = FontScaler.getNullScaler();
202 return getGlyphImage(pScalerContext, glyphCode);
203 }
204 }
205
206 Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext, int glyphCode) {
207 try {
208 return getScaler().getGlyphOutlineBounds(pScalerContext, glyphCode);
209 } catch (FontScalerException fe) {
210 scaler = FontScaler.getNullScaler();
211 return getGlyphOutlineBounds(pScalerContext, glyphCode);
212 }
213 }
214
215 GeneralPath getGlyphOutline(long pScalerContext, int glyphCode, float x, float y) {
216 try {
217 return getScaler().getGlyphOutline(pScalerContext, glyphCode, x, y);
218 } catch (FontScalerException fe) {
219 scaler = FontScaler.getNullScaler();
220 return getGlyphOutline(pScalerContext, glyphCode, x, y);
221 }
222 }
223
224 GeneralPath getGlyphVectorOutline(long pScalerContext, int[] glyphs, int numGlyphs, float x, float y) {
225 try {
226 return getScaler().getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y);
227 } catch (FontScalerException fe) {
228 scaler = FontScaler.getNullScaler();
229 return getGlyphVectorOutline(pScalerContext, glyphs, numGlyphs, x, y);
230 }
231 }
232
233 /* T1 & TT implementation differ so this method is abstract.
234 NB: null should not be returned here! */
235 protected abstract FontScaler getScaler();
236
237 protected long getUnitsPerEm() {
238 return getScaler().getUnitsPerEm();
239 }
240
241 private static class CreatedFontFileDisposerRecord
242 implements DisposerRecord {
243
244 File fontFile = null;
245 CreatedFontTracker tracker;
246
247 private CreatedFontFileDisposerRecord(File file,
248 CreatedFontTracker tracker) {
249 fontFile = file;
250 this.tracker = tracker;
251 }
252
253 public void dispose() {
254 java.security.AccessController.doPrivileged(
255 new java.security.PrivilegedAction() {
256 public Object run() {
257 if (fontFile != null) {
258 try {
259 if (tracker != null) {
260 tracker.subBytes((int)fontFile.length());
261 }
262 /* REMIND: is it possible that the file is
263 * still open? It will be closed when the
264 * font2D is disposed but could this code
265 * execute first? If so the file would not
266 * be deleted on MS-windows.
267 */
268 fontFile.delete();
269 /* remove from delete on exit hook list : */
270 // FIXME: still need to be refactored
271 SunFontManager.getInstance().tmpFontFiles.remove(fontFile);
272 } catch (Exception e) {
273 }
274 }
275 return null;
276 }
277 });
278 }
279 }
280
281 protected String getPublicFileName() {
282 SecurityManager sm = System.getSecurityManager();
283 if (sm == null) {
284 return platName;
285 }
286 boolean canReadProperty = true;
287
288 try {
289 sm.checkPropertyAccess("java.io.tmpdir");
290 } catch (SecurityException e) {
291 canReadProperty = false;
292 }
293
294 if (canReadProperty) {
295 return platName;
296 }
297
298 final File f = new File(platName);
299
300 Boolean isTmpFile = Boolean.FALSE;
301 try {
302 isTmpFile = AccessController.doPrivileged(
303 new PrivilegedExceptionAction<Boolean>() {
304 public Boolean run() {
305 File tmp = new File(System.getProperty("java.io.tmpdir"));
306 try {
307 String tpath = tmp.getCanonicalPath();
308 String fpath = f.getCanonicalPath();
309
310 return (fpath == null) || fpath.startsWith(tpath);
311 } catch (IOException e) {
312 return Boolean.TRUE;
313 }
314 }
315 }
316 );
317 } catch (PrivilegedActionException e) {
318 // unable to verify whether value of java.io.tempdir will be
319 // exposed, so return only a name of the font file.
320 isTmpFile = Boolean.TRUE;
321 }
322
323 return isTmpFile ? "temp file" : platName;
324 }
325 }